package com.spark.gaea.crypto;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Maps.newHashMap;

public class Hmacer {
	private static final Logger LOG;
	private static final Integer MAX_AGE_OFFSET;
	private static final Integer MAX_AGE;
	private static final String ALGORITHM = "HmacSHA1";
	public static final String SIGNATURE_KEY = "_signature_";
	public static final String TIMESTAMP_KEY = "_timestamp_";

	public static String getTimestamp() {
		return String.valueOf(System.currentTimeMillis() / 1000L);
	}

	public static Map<String, String> convertRequest(final Map<String, String[]> stringMap) {
		final Map<String, String> params = newHashMap();
		for (final String key : stringMap.keySet()) {
			final String[] values = stringMap.get(key);
			if (values != null && values.length > 0) {
				params.put(key, values[0]);
			}
		}
		return params;
	}

	public static String signRequest(final String secretKey, final Map<String, String> params) {
		try {
			final Mac mac = Mac.getInstance("HmacSHA1");
			final SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
			mac.init(keySpec);
			mac.update(sortParam(params).getBytes());
			final byte[] encryptedBytes = mac.doFinal();
			return Base64.encodeBase64URLSafeString(encryptedBytes);
		} catch (Exception e) {
			Hmacer.LOG.error("\u65e0\u6cd5\u5bf9\u8bf7\u6c42\u7b7e\u540d");
			return null;
		}
	}

	public static String sortParam(final Map<String, String> params) {
		final List<String> sortedParams = new ArrayList<String>();
		for (final String key : params.keySet()) {
			sortedParams.add(key.toLowerCase() + "=" + params.get(key));
		}
		Collections.sort(sortedParams);
		String sortedUrl = null;
		boolean first = true;
		for (final String param : sortedParams) {
			if (first) {
				sortedUrl = param;
				first = false;
			} else {
				sortedUrl = sortedUrl + "&" + param;
			}
		}
		return sortedUrl;
	}

	public static boolean authenticate(final String secretKey, final Map<String, String> params) {
		final String signature = params.get("_signature_");
		params.remove("_signature_");
		if (StringUtils.isBlank(signature)) {
			Hmacer.LOG.warn("\u53c2\u6570\u4e2d\u6ca1\u6709\u627e\u5230\u7b7e\u540d\u6216\u7b7e\u540d\u4e3a\u7a7a");
			return false;
		}
		long timestamp;
		try {
			timestamp = Long.parseLong(params.get("_timestamp_"));
		} catch (NumberFormatException e) {
			Hmacer.LOG.warn("\u65f6\u95f4\u6233\u683c\u5f0f\u9519\u8bef");
			return false;
		}
		if (timestamp <= 0L) {
			Hmacer.LOG
					.warn("\u53c2\u6570\u4e2d\u6ca1\u6709\u627e\u5230\u65f6\u95f4\u6233\u6216\u8005\u65f6\u95f4\u6233\u4e3a\u5c0f\u4e8e0");
			return false;
		}
		final long reqAge = System.currentTimeMillis() / 1000L - timestamp;
		if (reqAge < Hmacer.MAX_AGE_OFFSET) {
			Hmacer.LOG.warn("\u65f6\u95f4\u6233\u8d85\u51fa\u5141\u8bb8\u7684\u8303\u56f4");
			return false;
		}
		if (reqAge > Hmacer.MAX_AGE) {
			Hmacer.LOG.warn("\u7b7e\u540d\u5df2\u8fc7\u671f");
			return false;
		}
		return signature.equals(signRequest(secretKey, params));
	}

	public static String generateSecretKey() {
		try {
			final KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
			final SecretKey key = generator.generateKey();
			return Base64.encodeBase64URLSafeString(key.getEncoded());
		} catch (NoSuchAlgorithmException e) {
			Hmacer.LOG.warn("\u751f\u6210SecretKey\u51fa\u73b0\u5f02\u5e38", e);
			return null;
		}
	}

	static {
		LOG = Logger.getLogger(Hmacer.class);
		MAX_AGE_OFFSET = -10;
		MAX_AGE = 300;
	}
}
